<?php
/**
 * 
 * @author yusaint
 * @since 2014-3-9
 * @project Pfinal
 */
class Pfinal_Model_Protocol_RMDBS{
	
	const OPT_TYPE_DDL = 0;
	const OPT_TYPE_INSERT = 1;
	const OPT_TYPE_SELECT = 2;
	const OPT_TYPE_UPDATE = 3;
	
	const OPT_SELECT=' select ';
	
	const OPT_WHERE = ' where ';
	const OPT_FROM	=' from ';
	const OPT_AS = ' as ';
	const OPT_GROUPBY=' group by ';
	const OPT_ORDERBY=' order by ';
	const OPT_AND	=' and ';
	const OPT_OR	=' or ';
	const OPT_LIMIT=' limit ';
	//ddl操作的meta信息
	const OPT_DESC = 'desc';
	const OPT_TRUNCATE = 'truncate';
	const OPT_DROP = 'drop';
	
	protected $optType;
	
	//ddl操作
	protected $ddlHandler;
	
	//insert操作
	const OPT_INSERT = 'insert into ';

	const OPT_UPDATE = ' update ';
	
	protected $tableName;
	protected $alias;
	protected $orderClause = array();
	protected $groupClause = array();
	protected $count;
	protected $offset;
	protected $fields = array();
	protected $whereClause=array();
	protected $_on = array();
	protected $joinTable = array();
	
	//insert操作
	protected $kSet;
	protected $vSet;
	
	
	
	protected $stm;
	
	/**
	 * @return the $kSet
	 */
	public function getKSet() {
		return $this->kSet;
	}

	/**
	 * @return the $vSet
	 */
	public function getVSet() {
		return $this->vSet;
	}

	/**
	 * @param field_type $kSet
	 */
	public function setKSet($kSet) {
		$this->kSet = $kSet;
	}

	/**
	 * @param field_type $vSet
	 */
	public function setVSet($vSet) {
		$this->vSet = $vSet;
	}

	/**
	 * @param field_type $optType
	 */
	public function setOptType($optType) {
		$this->optType = $optType;
	}

	public function setTableName($tableName,$alias){
		$this->tableName = $tableName;
		$this->alias = $alias;
	}
	/**
	 * 注意要加表名限定
	 * @param unknown_type $fields
	 */
	public function addFields($field){
		if (!is_array($field)) {
			$field = array($field);
		}
		$this->fields = array_merge($this->fields,$field);
	}
	
	public function addGroupBy($groupby){
		if (!empty($groupby))
			$this->groupClause[] = array_merge($this->groupClause,$groupby);
	}
	
	public function addOrderBy($order){
		if (!empty($order))
			$this->orderClause[] = array_merge($this->orderClause,$order);
	}
	
	public function addWhere($where){
		if (!empty($where))
			$this->whereClause = array_merge($this->whereClause,$where);
	}
	
	public function limit($offset,$count){
		$this->offset = $offset;
		$this->count = $count;
	}
	
	public function addJoin($opt,$tableName,$on){
		$this->joinTable[] = array(
				$opt,
				$tableName,
				$on
			);
	}
	/**
	 * @return string|NULL
	 */
	protected function _where(){
		if(isset($this->whereClause)&&!empty($this->whereClause)){
			$stm =self::OPT_WHERE;
			$isFirst=true;
			//第一个条件不容许为or
			$first=reset($this->whereClause);
			if($first[0]==self::OPT_OR){
				throw new Pfinal_Exception_Runtime('invalid where clause,the first where could not be or');
			}
			$isOr = false;
			$stackDepth = 0;
			foreach ($this->whereClause as $where){			
				if($isFirst){
					$stm.=$where[1];
					$isFirst=false;
				}else{//如果是or操作符，这里要成为一个整体
					$current = trim(strtolower($where[0]));
					$target = trim(strtolower(self::OPT_OR));
					if ($current==$target) {
						$stackDepth++;//栈深度
						$isOr = true;
						if ($stackDepth==1) {
							$stack .= self::OPT_AND. '('.$where[1].' ';
						}else{
							$stack .= $where[0].' '.$where[1];
						}
						continue;
					}else{
						$isOr = false;
						if (!empty($stack)) {
							$stack .= ')';
							$stm .= $stack;
							$stack = '';
							$stackDepth = 0;
						}
						
					}				
					$stm.=' '.$where[0].' '.$where[1].' ';
				}
			}
			if ($isOr) {
				$stm .= $stack.')';
			}
			return $stm;
		}
		else
			return NULL;
	}
	
	/**
	 * @return NULL
	 */
	protected function _join()
	{
		if (empty($this->joinTable))
			return null;
		else {
			$stm = ' ';
			foreach ($this->joinTable as $join) {
				/***
				 array(
				 		self::OPT_LEFT_JOIN,
				 		array($tableName,$alias),
				 		$on,
				 )
				*
				*/
				$joinTo = $join[1];
				if (isset($joinTo[1])) { //alias is set
					$table = $joinTo[0] . ' as ' . $joinTo[1];
				} else
					$table = $joinTo[0];
				$stm .= $join[0] . ' ' . $table . ' on ' . $join[2] . ' ';
			}
			return $stm;
		}
	}
	protected function _group(){
		if(!empty($this->groupClause))
			return self::OPT_GROUPBY . implode(',', $this->groupClause);
		else
			return null;
	}
	protected function _order(){
		if (!empty($this->orderClause))
			return self::OPT_ORDERBY . implode(',', $this->orderClause[0]);
		else
			return null;
	}
	protected function _limit()
	{
		if (isset($this->offset)&&isset($this->count))
			return self::OPT_LIMIT . implode(',', array($this->offset, $this->count));
		else if(isset($this->count))
			return self::OPT_LIMIT . $this->count;
		else
			return null;
	}
	protected function _what()
	{
		if (!isset($this->fields) || empty($this->fields)) {
			return '*';
		}
		$this->fields = array_unique($this->fields);
		
		return implode(',', $this->fields);
	}
	protected function _from()
	{
		if (!$this->tableName) {
			throw new Pfinal_Exception_Runtime('invalid statement,table name is missing');
		}
		$stm = self::OPT_FROM . $this->tableName;
		if (!empty($this->alias)){
			$stm .= self::OPT_AS.$this->alias;
		}
		return $stm;
	}
	
	public function assemble(){
		switch ($this->optType){
			case self::OPT_TYPE_SELECT:
				return self::OPT_SELECT . ' ' . $this->_what() . $this->_from() . $this->
				_join() . $this->_where() . $this->_group() . $this->_order() . $this->_limit();
				break;
			case self::OPT_TYPE_INSERT:
				return $this->assembleInsert();
			case self::OPT_TYPE_DDL:
				return $this->ddlHandler.' '.$this->tableName;
			case self::OPT_TYPE_UPDATE:
				return $this->assembleUpdate();
		}
		
	}
	
	/**
	 * 
	 * @return string
	 */
	protected function assembleInsert(){
		if (empty($this->kSet)||empty($this->vSet)){
			throw new Pfinal_Exception_Runtime("empty kset or vset given while try to insert into {$this->tableName}");
		}
		$stm = self::OPT_INSERT;//insert into
		$stm .= $this->tableName;
		$stm .= '('.implode(',', $this->kSet).')';
		$stm .= ' values ';
		$stm .= '('.implode(',', $this->vSet).')';
		return $stm;
		
	}

	/**
	 * [assembleUpdate description]
	 * @return [type] [description]
	 */
	protected function assembleUpdate(){
		if (empty($this->kSet)||empty($this->vSet)){
			throw new Pfinal_Exception_Runtime("empty kset or vset given while try to update {$this->tableName}");
		}
		$stm = self::OPT_UPDATE;//Update
		$stm .= $this->tableName;
		$stm .= ' set ';
		$kv = array();
		foreach ($this->kSet as $key=>$value) {
			$kv[] = $value.'='.$this->vSet[$key];
		}
		$stm .= implode(',', $kv);
		$stm .= $this->_where();
		return $stm;
	}
	/**
	 * @return the $ddlHandler
	 */
	public function getDdlHandler() {
		return $this->ddlHandler;
	}

	/**
	 * @param field_type $ddlHandler
	 */
	public function setDdlHandler($ddlHandler) {
		$this->ddlHandler = $ddlHandler;
	}
	
}